#include "stdafx.h"
#include "..\headers\PhysicsComponents.h"
#include "..\headers\VisualComponents.h"
#include "..\headers\PhysicsWorld.h"
#include "..\headers\nMotionState.h"
#include "..\headers\GameObject.h"
#include "ext\btOgre\BtOgreGP.h"

namespace ne{

float RigidBodyComponent::s_defaultGravity = -9.8f; 

RigidBodyComponent::RigidBodyComponent():
	m_physicsWorld(nullptr),
	m_mass(0.0f),
	m_isInContact(false){ 
	m_type|=COMPONENT_RIGIDBODY;
}
RigidBodyComponent::~RigidBodyComponent(){delete m_collisionData;m_collisionData=nullptr;m_owner->Unregister(this);}

void RigidBodyComponent::Init() {
	m_physicsWorld=GameObject::GetPhysicsWorld();
	m_collisionData = new GameObjectCollisionData(m_owner,this);
	MotionState* motionState=new MotionState(m_owner->GetSceneNode());
	m_compoundShape=new btCompoundShape;
	m_mass=0.0f;
	m_rigidBody=new btRigidBody(m_mass,motionState,m_compoundShape);
	m_physicsWorld->GetDynamicWorld()->addRigidBody(m_rigidBody);
	m_rigidBody->setUserPointer(m_collisionData);
	btTransform transform;
	transform.setIdentity();
	transform.setRotation(Convert::ToBullet(m_owner->GetSceneNode()->getOrientation()));
	transform.setOrigin(Convert::ToBullet(m_owner->GetSceneNode()->getPosition()));
	m_rigidBody->setWorldTransform(transform);
	m_rigidBody->setActivationState(DISABLE_DEACTIVATION);
	m_owner->Register(this);
}

void RigidBodyComponent::Shut(){
	//if(m_isInContact){
		m_physicsWorld->RemoveContact(this);
	//}
	CEvent evt(NE_MSG_REMOVE_COLLIDER);
	m_owner->Notify(&evt);
	if (m_rigidBody){
		delete m_rigidBody->getMotionState();
		m_physicsWorld->GetDynamicWorld()->removeRigidBody(m_rigidBody);
		delete m_rigidBody;
		m_rigidBody = nullptr;
	}
	if (m_compoundShape){
		delete m_compoundShape;
		m_compoundShape = nullptr;
	}
}

void RigidBodyComponent::Notify(CEvent* evt){
	if (evt->m_type==NE_MSG_SET_POSITION){
		Ogre::Vector3& pos = static_cast<CEventVec3*>(evt)->m_vec3;
		m_rigidBody->getWorldTransform().setOrigin(Convert::ToBullet(pos));
	}
}

void RigidBodyComponent::ApplyImpulse(const Ogre::Vector3& impulse, const Ogre::Vector3& relPos){
	m_rigidBody->applyImpulse(Convert::ToBullet(impulse),Convert::ToBullet(relPos));
}

void RigidBodyComponent::ApplyImpulse(const Ogre::Vector3& impulse){
	m_rigidBody->applyCentralImpulse(Convert::ToBullet(impulse));
}

void RigidBodyComponent::SetFriction(float friction){m_rigidBody->setFriction(friction);}
void RigidBodyComponent::SetRestitution(float restitution){m_rigidBody->setRestitution(restitution);}
float RigidBodyComponent::GetRestitution()const{return m_rigidBody->getRestitution();}
float RigidBodyComponent::GetFriction()const{return m_rigidBody->getFriction();}

void RigidBodyComponent::SetMass(float mass){
	m_physicsWorld->GetDynamicWorld()->removeRigidBody(m_rigidBody);
	btVector3 inertia;
	m_compoundShape->calculateLocalInertia(mass,inertia);
	m_rigidBody->setMassProps(mass,inertia);
	m_physicsWorld->GetDynamicWorld()->addRigidBody(m_rigidBody);
}

bool RigidBodyComponent::IsStatic(){
	return ((m_rigidBody->getFlags()&btCollisionObject::CF_STATIC_OBJECT)==btCollisionObject::CF_STATIC_OBJECT);
}

void RigidBodyComponent::SetStatic(bool p_value){
	m_physicsWorld->GetDynamicWorld()->removeRigidBody(m_rigidBody);
	if(!p_value){m_rigidBody->setFlags(m_rigidBody->getFlags()&~btCollisionObject::CF_STATIC_OBJECT);}
	else{m_rigidBody->setFlags(m_rigidBody->getFlags()|btCollisionObject::CF_STATIC_OBJECT);}
	m_physicsWorld->GetDynamicWorld()->addRigidBody(m_rigidBody);
}

float RigidBodyComponent::GetMass()const{return m_rigidBody->getInvMass();}
const Ogre::Vector3 RigidBodyComponent::GetGravity()const{return Convert::ToOgre(m_rigidBody->getGravity());}
void RigidBodyComponent::SetGravity(float x,float y,float z){m_rigidBody->setGravity(btVector3(x,y,z));}
void RigidBodyComponent::SetGravity(const Ogre::Vector3& gravity){m_rigidBody->setGravity(Convert::ToBullet(gravity));}

CharacterController::CharacterController(Ogre::SceneNode* node,btPairCachingGhostObject* ghostObject,btConvexShape* convexShape,btScalar stepHeight,int upAxis):
	btKinematicCharacterController(ghostObject,convexShape,stepHeight,upAxis),m_node(node),m_offset(Ogre::Vector3::ZERO){}
CharacterController::~CharacterController(void){}

void CharacterController::updateAction(btCollisionWorld* collisionWorld,btScalar dt){
	btKinematicCharacterController::updateAction(collisionWorld,dt);
	m_node->setPosition(Convert::ToOgre(m_ghostObject->getWorldTransform().getOrigin())+m_offset);
}

CharacterControllerComponent::CharacterControllerComponent():
	m_physicsWorld(nullptr),
	m_ghostObject(nullptr),
	m_convexShape(nullptr),
	m_standingConvexShape(nullptr),
	m_crouchingConvexShape(nullptr),
	m_isInContact(false),
	m_offset(Ogre::Vector3::ZERO){}

CharacterControllerComponent::~CharacterControllerComponent(void){Shut();}

void CharacterControllerComponent::Init() {
	m_physicsWorld=GameObject::GetPhysicsWorld();
	m_collisionData = new GameObjectCollisionData(m_owner,this);
}

void CharacterControllerComponent::Create(){
	Component* comp = m_owner->GetComponent(COMPONENT_MESH);
	if (comp!=nullptr&&m_convexShape==nullptr){
		BtOgre::StaticMeshToShapeConverter converter(static_cast<MeshComponent*>(comp)->GetEntity());
		m_convexShape=converter.createCapsule();
		m_standingConvexShape=m_convexShape;
		btScalar halfHeight=static_cast<btCapsuleShape*>(m_convexShape)->getHalfHeight()/3;
		btScalar radius=static_cast<btCapsuleShape*>(m_convexShape)->getRadius();
		m_crouchingConvexShape=new btCapsuleShape(radius,halfHeight);
		FinalizeController();
	}
	else {
		System::Log("Warning: CharacterController is missing a mesh component, creating capsule with default 1.0f,1.0f size");
		Create(1.0f,1.0f);
	}
}

void CharacterControllerComponent::Create(float radius,float halfHeight){
	m_convexShape=new btCapsuleShape(radius,halfHeight);
	m_standingConvexShape=m_convexShape;
	m_crouchingConvexShape=new btCapsuleShape(radius,halfHeight/3);
	FinalizeController();
}

void CharacterControllerComponent::FinalizeController(){
	m_ghostObject=new btPairCachingGhostObject;
	m_ghostObject->setCollisionShape(m_convexShape);
	m_ghostObject->setCollisionFlags(m_ghostObject->getCollisionFlags()|btCollisionObject::CF_CHARACTER_OBJECT);
	m_physicsWorld->GetDynamicWorld()->addCollisionObject(m_ghostObject,btBroadphaseProxy::CharacterFilter,btBroadphaseProxy::StaticFilter|btBroadphaseProxy::DefaultFilter);
	m_cc=new btKinematicCharacterController(m_ghostObject,m_convexShape,0.15f);
	m_physicsWorld->GetDynamicWorld()->addAction(m_cc);
	m_ghostObject->setUserPointer(m_collisionData);
}

void CharacterControllerComponent::Shut(){
	if(m_isInContact){
		m_physicsWorld->RemoveContact(this);
	}
	if(m_ghostObject){
		m_physicsWorld->GetDynamicWorld()->removeCollisionObject(m_ghostObject);
		delete m_ghostObject;
		m_ghostObject=nullptr;
	}
	if(m_convexShape){
		delete m_convexShape;
		m_convexShape=nullptr;
	}
	if(m_crouchingConvexShape){
		delete m_crouchingConvexShape;
		m_crouchingConvexShape=nullptr;
	}
	m_physicsWorld->GetDynamicWorld()->removeAction(m_cc);
}

void CharacterControllerComponent::Notify(CEvent* evt){

}

void CharacterControllerComponent::SetShapeOffset(const Ogre::Vector3& p_offset){m_offset=p_offset;}
void CharacterControllerComponent::SetWalkDirection(float x,float y,float z){
	m_cc->setWalkDirection(btVector3(x,y,z));
}

void CharacterControllerComponent::SetWalkDirection(const Ogre::Vector3& dir){
	m_cc->setWalkDirection(Convert::ToBullet(dir));
}

void CharacterControllerComponent::SetGravity(float gravity){m_cc->setGravity(btScalar(gravity));}
void CharacterControllerComponent::SetFallSpeed(float fallSpeed){m_cc->setFallSpeed(btScalar(fallSpeed));}
void CharacterControllerComponent::SetMaxSlope(float maxSlope){m_cc->setMaxSlope(btScalar(maxSlope));}
void CharacterControllerComponent::SetMaxJumpHeight(float maxJumpHeight){m_cc->setMaxJumpHeight(btScalar(maxJumpHeight));}
void CharacterControllerComponent::SetJumpSpeed(float jumpSpeed){m_cc->setJumpSpeed(btScalar(jumpSpeed));}

void CharacterControllerComponent::Jump(){m_cc->jump();}

void CharacterControllerComponent::LateUpdate(GameTime* gameTime){
	m_owner->GetSceneNode()->setPosition(Convert::ToOgre(m_ghostObject->getWorldTransform().getOrigin())+m_offset);
}

}